package org.jetlinks.pro.device.service;

import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
import org.hswebframework.web.crud.service.ReactiveTreeSortEntityService;
import org.jetlinks.core.utils.FluxUtils;
import org.jetlinks.pro.assets.*;
import org.jetlinks.pro.assets.event.AssetsBindEvent;
import org.jetlinks.pro.assets.event.AssetsUnBindEvent;
import org.jetlinks.pro.assets.impl.terms.DimensionAssetsTerm;
import org.jetlinks.pro.assets.impl.terms.DimensionAssetsTermBuilder;
import org.jetlinks.pro.device.entity.DeviceCategoryEntity;
import org.jetlinks.pro.device.entity.DeviceInstanceEntity;
import org.jetlinks.pro.device.entity.DeviceProductEntity;
import org.jetlinks.pro.device.tenant.DeviceAssetType;
import org.jetlinks.pro.tenant.TenantAssetManager;
import org.jetlinks.pro.tenant.event.TenantAssetsBindEvent;
import org.jetlinks.pro.tenant.event.TenantAssetsUnBindEvent;
import org.jetlinks.pro.tenant.term.AssetsTerm;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.stream.Collectors;

/**
 * 设备相关资产自动绑定器,用于在进行资产绑定解绑的同时,对关联对相关数据也进行绑定解绑.
 * 比如: 绑定设备时,自动绑定产品。绑定产品时,自动绑定产品分类
 *
 * @author zhouhao
 * @since 1.11
 */
@Component
@AllArgsConstructor
public class DeviceAssetAutoBinder {

    private final ReactiveRepository<DeviceInstanceEntity,String> deviceRepository;
    private final ReactiveRepository<DeviceProductEntity,String> productRepository;
    private final ReactiveTreeSortEntityService<DeviceCategoryEntity,String> categoryService;

    private final AssetBindManager assetBindManager;
    private final TenantAssetManager tenantAssetManager;

    //其他维度的资产绑定
    @EventListener
    @SuppressWarnings("all")
    public void handleAssetsBind(AssetsBindEvent event) {
        //绑定设备时自动绑定产品
        if (DeviceAssetType.device.getId().equals(event.getAssetType())) {
            event.async(AssetsUtils.doWhenSourceIsManualBind(
                Mono.defer(() -> {
                    return deviceRepository
                        .createQuery()
                        .select(DeviceInstanceEntity::getProductId)
                        .where()
                        .in(DeviceInstanceEntity::getId, event.getAssetId())
                        .fetch()
                        .map(DeviceInstanceEntity::getProductId)
                        .distinct()
                        .collectList()
                        .flatMap(productList -> assetBindManager
                            .bindAssets(Mono.just(AssetBindRequest
                                                      .builder()
                                                      .assetType(DeviceAssetType.product.getId())
                                                      .assetIdList(productList)
                                                      .targetType(event.getTargetType())
                                                      .targetId(event.getTargetId())
                                                      .relation(event.getRelation())
                                                      .permission(event.getPermission())
                                                      .allPermission(event.isAllPermission())
                                                      .build()))
                        );
                }), true));
        }
        //#JZ-552 绑定产品时自动绑定产品分类
        if (DeviceAssetType.product.getId().equals(event.getAssetType())) {
            event.async(AssetsUtils.doWhenSourceIsManualBind(
                Flux.defer(() -> {
                    return productRepository
                        .createQuery()
                        .in(DeviceProductEntity::getId, event.getAssetId())
                        .select(DeviceProductEntity::getClassifiedId)
                        .fetch()
                        .as(FluxUtils.safeMap(DeviceProductEntity::getClassifiedId))
                        .filter(StringUtils::hasText)
                        .distinct()
                        .collectList()
                        .filter(CollectionUtils::isNotEmpty)
                        .flatMap(list -> {
                            //查询出所有父节点也进行绑定
                            return categoryService
                                .queryIncludeParent(list)
                                .map(DeviceCategoryEntity::getId)
                                .distinct()
                                .collectList()
                                .flatMap(includeParent -> {
                                    return assetBindManager
                                        .bindAssets(Mono.just(
                                            AssetBindRequest
                                                .builder()
                                                .assetType(DeviceAssetType.deviceCategory.getId())
                                                .targetType(event.getTargetType())
                                                .targetId(event.getTargetId())
                                                .assetIdList(includeParent)
                                                .relation(event.getRelation())
                                                .permission(event.getPermission())
                                                .allPermission(event.isAllPermission())
                                                .build()
                                        ));
                                });
                        });
                }), true)
            );
        }
    }

    @EventListener
    @SuppressWarnings("all")
    public void handleUnBindEvent(AssetsUnBindEvent event) {
        //解绑分类时,同时解绑产品
        if (DeviceAssetType.deviceCategory.getId().equals(event.getAssetType())) {
            event.async(
                AssetsUtils.doWhenSourceIsManualBind(
                    Flux.defer(() -> {
                        return productRepository
                            .createQuery()
                            .select(DeviceProductEntity::getId)
                            .in(DeviceProductEntity::getClassifiedId, event.getAssetId())
                            //是指定用户的产品
                            .and(DeviceInstanceEntity::getId,
                                 DimensionAssetsTermBuilder.termType,
                                 DimensionAssetsTerm.of(Arrays.asList(
                                     DimensionInfo.of(event.getTargetType(), event.getTargetId())
                                 ), DeviceAssetType.product.getId()))
                            .fetch()
                            .map(DeviceProductEntity::getId)
                            .collectList()
                            .filter(CollectionUtils::isNotEmpty)
                            .flatMap(list -> {
                                //解绑资产
                                return assetBindManager
                                    .unbindAssets(Mono.just(
                                        AssetUnbindRequest
                                            .builder()
                                            .assetType(DeviceAssetType.product.getId())
                                            .targetType(event.getTargetType())
                                            .targetId(event.getTargetId())
                                            .assetIdList(list)
                                            .build()
                                    ));
                            });
                    })
                    , true
                )

            );
        }
        //解绑产品时,同时解绑设备
        if (DeviceAssetType.product.getId().equals(event.getAssetType())) {
            event.async(
                AssetsUtils.doWhenSourceIsManualBind(
                    Flux
                        .defer(() -> {
                            return deviceRepository
                                .createQuery()
                                .select(DeviceInstanceEntity::getId)
                                .where()
                                //产品ID
                                .in(DeviceInstanceEntity::getProductId, event.getAssetId())
                                .and(DeviceInstanceEntity::getId,
                                     DimensionAssetsTermBuilder.termType,
                                     DimensionAssetsTerm.of(Arrays.asList(
                                         DimensionInfo.of(event.getTargetType(), event.getTargetId())
                                     ), DeviceAssetType.device.getId()))
                                .fetch()
                                .map(DeviceInstanceEntity::getId)
                                .buffer(200)
                                .flatMap(list -> {
                                    //解绑资产
                                    return assetBindManager
                                        .unbindAssets(Mono.just(
                                            AssetUnbindRequest
                                                .builder()
                                                .assetType(DeviceAssetType.device.getId())
                                                .targetType(event.getTargetType())
                                                .targetId(event.getTargetId())
                                                .assetIdList(list)
                                                .build()
                                        ));
                                });
                        }),
                    true)
            )
            ;
        }
    }

    //租户资产绑定
    @EventListener
    @SuppressWarnings("all")
    public void handleAssetsBind(TenantAssetsBindEvent event) {
        //绑定设备时自动绑定产品
        if (DeviceAssetType.device.getId().equals(event.getAssetType())) {
            event.async(
                AssetsUtils.doWhenSourceIsManualBind(
                    Mono.defer(
                        () -> deviceRepository
                            .createQuery()
                            .select(DeviceInstanceEntity::getProductId)
                            .where()
                            .in(DeviceInstanceEntity::getId, event.getAssetId())
                            .fetch()
                            .map(DeviceInstanceEntity::getProductId)
                            .distinct()
                            .collectList()
                            .flatMap(productList -> tenantAssetManager
                                .bindAssetsOwner(event.getTenantId(),
                                                 DeviceAssetType.product,
                                                 event.getUserId(), productList))
                    ), true));
        }
        //绑定产品时,自动绑定分类
        if (DeviceAssetType.product.getId().equals(event.getAssetType())) {
            event.async(
                AssetsUtils
                    .doWhenSourceIsManualBind(
                        Flux.defer(() -> {
                            return productRepository
                                .createQuery()
                                .select(DeviceProductEntity::getClassifiedId)
                                .fetch()
                                .as(FluxUtils.safeMap(DeviceProductEntity::getClassifiedId))
                                .filter(StringUtils::hasText)
                                .collect(Collectors.toSet())
                                .filter(CollectionUtils::isNotEmpty)
                                .flatMap(list -> {
                                    //查询出所有父节点也进行绑定
                                    return categoryService
                                        .queryIncludeParent(list)
                                        .map(DeviceCategoryEntity::getId)
                                        .distinct()
                                        .collectList()
                                        .flatMap(includeParent -> {
                                            return tenantAssetManager
                                                .bindAssetsOwner(event.getTenantId(),
                                                                 DeviceAssetType.deviceCategory,
                                                                 event.getUserId(),
                                                                 includeParent);
                                        });
                                });
                        }),
                        true)
            );
        }
    }

    @EventListener
    @SuppressWarnings("all")
    public void handleUnBindEvent(TenantAssetsUnBindEvent event) {
        //解绑分类时,解绑产品
        if (DeviceAssetType.deviceCategory.getId().equals(event.getAssetType())) {
            event.async(
                AssetsUtils.doWhenSourceIsManualBind(
                    Flux.defer(() -> {
                        return productRepository
                            .createQuery()
                            .select(DeviceProductEntity::getId)
                            .in(DeviceProductEntity::getClassifiedId, event.getAssetId())
                            //是指定用户的产品
                            .and(DeviceInstanceEntity::getId,
                                 AssetsTerm.ID,
                                 AssetsTerm.of(event.getTenantId(), DeviceAssetType.product.getId(), event.getUserId(), false))
                            .fetch()
                            .map(DeviceProductEntity::getId)
                            .collectList()
                            .filter(CollectionUtils::isNotEmpty)
                            .flatMap(list -> {
                                //解绑资产
                                return tenantAssetManager
                                    .unbindAssetsOwner(event.getTenantId(),
                                                       DeviceAssetType.product,
                                                       event.getUserId(),
                                                       list);
                            });
                    }),
                    true
                )

            );
        }
        //解绑产品时解绑设备
        if (DeviceAssetType.product.getId().equals(event.getAssetType())) {
            event.async(
                AssetsUtils.doWhenSourceIsManualBind(
                    Flux.defer(() -> {
                        return deviceRepository
                            .createQuery()
                            .select(DeviceInstanceEntity::getId)
                            .where()
                            //产品ID
                            .in(DeviceInstanceEntity::getProductId, event.getAssetId())
                            //是当前用户的设备
                            .and(DeviceInstanceEntity::getId,
                                 AssetsTerm.ID,
                                 AssetsTerm.of(event.getTenantId(), DeviceAssetType.device.getId(), event.getUserId(), false))
                            .fetch()
                            .map(DeviceInstanceEntity::getId)
                            .buffer(200)
                            .flatMap(list -> {
                                //解绑资产
                                return tenantAssetManager
                                    .unbindAssetsOwner(event.getTenantId(),
                                                       DeviceAssetType.device,
                                                       event.getUserId(),
                                                       list);
                            });
                    }),
                    true)
            );
        }

    }

}
